/**
 * @file    wb32f10x_fmc.c
 * @author  WestberryTech Application Team
 * @version V0.1.5
 * @date    18-February-2021
 * @brief   This file provides all the FMC firmware functions.
 */

/* Includes ------------------------------------------------------------------*/
#include "wb32f10x_fmc.h"

/** @addtogroup WB32F10x_StdPeriph_Driver
  * @{
  */

/** @defgroup FMC
  * @brief FMC driver modules
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

/** @defgroup FMC_Private_Defines
  * @{
  */

// __ASM void FLASH_OP_EXEC_RAM(uint32_t code, uint32_t fmc_base)
// {
//   STR   R0, [R1, #0x00]   // FMC->CON = 0x00800080; # WR=1
//   NOP
// LB_CK
//   LDR   R0, [R1, #0x00]
//   LSLS  R0, R0, #24
//   BMI   LB_CK             // while(FMC->CON & FMC_CON_WR);
//   BX    LR
// }
/* Do not change if it is not necessary */
#define FLASH_OP_RAM_CODE {0xBF006008, 0x06006808, 0x4770D4FC}

/**
  * @}
  */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static const uint32_t pre_op_code[] = 
{
  0x4FF0E92D, 0x21034832, 0x210C6281, 0xF8DF62C1, 
  0x2100C0C4, 0x1000F8CC, 0xF44F4608, 0x1C40767A, 
  0xDBFC42B0, 0xF8CC2201, 0x20002000, 0x42B01C40, 
  0x4829DBFC, 0xF0436803, 0x60030380, 0x302C4826, 
  0xF4436803, 0x60036320, 0x46104691, 0x323C4A22, 
  0x468A6010, 0x49214608, 0x48216008, 0x0340F8D0, 
  0x25004F1E, 0x5107F3C0, 0x3BFFF04F, 0x22001F3F, 
  0x4610465C, 0xEA5F683B, 0xD10678C0, 0xD10142A3, 
  0xE0002401, 0x44222400, 0x1C40461C, 0xDBF12814, 
  0xD91B2A02, 0xD9012910, 0xE0003910, 0x480D2100, 
  0x68021F00, 0x627FF022, 0x5201EA42, 0xF8CC6002, 
  0x2000A000, 0x42B01C40, 0xF8CCDBFC, 0x20009000, 
  0x42B01C40, 0x1C6DDBFC, 0xDBD02D05, 0x8FF0E8BD, 
  0x40010000, 0x40010438, 0x40010C20, 0x4000B804, 
  0x1FFFF000
};
#define PRE_OP()  ((void(*)(void))((unsigned int)(pre_op_code) | 0x01))()

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/** @defgroup FMC_Private_Functions
  * @{
  */

/**
 * @brief  Execute FLASH operation.
 * @return 0 - OK,  1 - Failed
 * @note   Before calling this function, FHSI must be enabled.
 */
static uint32_t FLASH_OP_EXEC(uint32_t op)
{
  uint32_t flash_op_ram_code[] = FLASH_OP_RAM_CODE;

  /* Start FLASH operation and wait for a FLASH operation to complete  */
  RCC->PCLKENR = 0x01;
  FMC->CON = 0x7F5F0D40 | (op & 0x1F);    /* [14:8]=0x0D, WREN=1, [4:0]=op */
  FMC->KEY = 0x5188DA08;
  FMC->KEY = 0x12586590;
  ((void(*)(uint32_t, uint32_t))((unsigned int)(flash_op_ram_code) | 0x01))(0x00800080, FMC_BASE);

  RCC->PCLKENR = 0x00;
  /* Clear WREN and OP[4:0] bits */
  FMC->CON = 0x005F0000;

  if (FMC->STAT & FMC_STAT_ERR)
    return 1;   /* Any error occur */
  else
    return 0;   /* FLASH operation complete */
}

/**
 * @brief  Clear page latches.
 * @return 0 - OK,  1 - Failed
 * @note   Before calling this function, FHSI must be enabled.
 */
uint32_t FMC_ClearPageLatch(void)
{
  uint32_t ret;
  int state;
  state = __disable_irq();
  ret = FLASH_OP_EXEC(0x04);
  if (!state) {
    __enable_irq();
  }
  return ret;
}

/**
 * @brief  Erase a specified FLASH page.
 * @param  Page_Address: The page address to be erased.
 * @return 0 - OK,  1 - Failed
 * @note   Before calling this function, FHSI must be enabled.
 */
uint32_t FMC_ErasePage(uint32_t Page_Address)
{
  uint32_t ret;
  int state;
  state = __disable_irq();
  PRE_OP();
  FMC->ADDR = Page_Address;
  ret = FLASH_OP_EXEC(0x08);
  if (!state) {
    __enable_irq();
  }
  return ret;
}

/**
 * @brief  Erase a specified FLASH sector.
 * @param  Sector_Address: The sector address to be erased.
 * @return 0 - OK,  1 - Failed
 * @note   Before calling this function, FHSI must be enabled.
 */
uint32_t FMC_EraseSector(uint32_t Sector_Address)
{
  uint32_t ret;
  int state;
  state = __disable_irq();
  PRE_OP();
  FMC->ADDR = Sector_Address;
  ret = FLASH_OP_EXEC(0x09);
  if (!state) {
    __enable_irq();
  }
  return ret;
}

/**
 * @brief  Erase main flash memory.
 * @return 0 - OK,  1 - Failed
 * @note   Before calling this function, FHSI must be enabled.
 */
uint32_t FMC_EraseBulk(void)
{
  uint32_t ret;
  int state;
  state = __disable_irq();
  PRE_OP();
  FMC->ADDR = 0x08000000;
  ret = FLASH_OP_EXEC(0x1B);
  if (!state) {
    __enable_irq();
  }
  return ret;
}

/**
 * @brief  Programs the data to the specified page address.
 * @param  Page_Address: The page address to be programmed.
 * @return 0 - OK,  1 - Failed
 * @note   Before calling this function, FHSI must be enabled.
 */
uint32_t FMC_ProgramPage(uint32_t Page_Address)
{
  uint32_t ret;
  int state;
  state = __disable_irq();
  PRE_OP();
  FMC->ADDR = Page_Address;
  ret = FLASH_OP_EXEC(0x0C);
  if (!state) {
    __enable_irq();
  }
  return ret;
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */
